#include "ThreadPool.h"
#include "Thread.hpp"
#include "Runnable.h"
#include <memory>
#include <queue>
#include <mutex>
#include <thread>
#include <atomic>

namespace sh
{
typedef std::unique_ptr<std::thread> ThreadHandle;

class RunnablePack
{
public:
    RunnablePack(Runnable *runnable, int priority)
        : runnable_(runnable), priority(priority)
    {
    }

    friend bool operator<(const RunnablePack &p1, const RunnablePack &p2)
    {
        return p1.priority < p2.priority;
    }

    Runnable *runnable_;
    int priority;
};

class ThreadPoolData
{
public:
    ThreadPoolData();
    ~ThreadPoolData();
private:
    friend class ThreadPool;
    std::priority_queue<RunnablePack> thread_queue_;//线程任务队列
    int current_thread_count_;
    int max_thread_count_;
    std::atomic_int active_thread_count_;
    std::mutex mutex_;
    std::vector<ThreadHandle> thread_vector_;
    bool finish_thread_;

private:
    void startThread();
    void run();
};
ThreadPoolData::ThreadPoolData()
    : current_thread_count_(0), max_thread_count_(0), active_thread_count_(0), finish_thread_(false)
{

}
ThreadPoolData::~ThreadPoolData()
{
    finish_thread_ = true;
    while (active_thread_count_ > 0)
    {
    }
}
void ThreadPoolData::startThread()
{
    if (current_thread_count_ >= max_thread_count_)
    {
        return;
    }
    for (; current_thread_count_ < max_thread_count_; current_thread_count_++)
    {
        ThreadHandle thread = std::unique_ptr<std::thread>(new std::thread(&ThreadPoolData::run, this));
        thread_vector_.push_back(std::move(thread));
    }
}
void ThreadPoolData::run()
{
    while (!finish_thread_)
    {
        mutex_.lock();
        if (thread_queue_.empty())
        {
            mutex_.unlock();
            Thread::msleep(100);
            continue;
        }
        Runnable *front = thread_queue_.top().runnable_;
        thread_queue_.pop();
        mutex_.unlock();
        if (nullptr != front)
        {
            active_thread_count_++;
            front->run();
            if (front->autoDelete())
            {
                delete front;
            }
            active_thread_count_--;
        }
    }
}
ThreadPool::ThreadPool()
    : data_(std::unique_ptr<ThreadPoolData>(new ThreadPoolData()))
{

}
ThreadPool::~ThreadPool() = default;

void ThreadPool::start(Runnable *runnable, int priority)
{
    if (!runnable)
    {
        return;
    }
    std::lock_guard<std::mutex> lock_guard(data_->mutex_);
    data_->thread_queue_.emplace(runnable, priority);
}
void ThreadPool::clear()
{
    std::lock_guard<std::mutex> lock_guard(data_->mutex_);
    while (!data_->thread_queue_.empty())
    {
        const Runnable *front = data_->thread_queue_.top().runnable_;
        data_->thread_queue_.pop();
        if (nullptr != front && front->autoDelete())
        {
            delete front;
        }
    }
}
void ThreadPool::setMaxThreadCount(int max_thread_count)
{
    data_->max_thread_count_ = max_thread_count;
    data_->startThread();
}
int ThreadPool::maxThreadCount()
{
    return data_->max_thread_count_;
}
}
